
////////////////////////////////////////////////////////////////////////////////
//
// Aranya Code
// author: TonyHack
// E-mail: tonyjobmails@gmail.com
//
// filename: url.cpp
// function: 成员函数的定义
//
////////////////////////////////////////////////////////////////////////////////

#include <ctype.h>
#include "url.h"

//////////////////////////////////////////////////////////////////////
/// 处理协议(默认去掉协议头)
/// @param url：url字符串
/// @param cut：是否把协议头从参数url字符串中去除
/// @return 是否成功解析
bool url::parse_scheme(char *url, bool cut)
{
	if(url)
	{
		_int_32 pos = 0;
		/// 找到url中第一个为':'的字符，	http://www.aranya.com
		while(url[pos] != 0 && url[pos] != ':')	++pos;

		/// ':' 后面两个字符是"//"，说明url中有协议字段
		if(url[pos] == ':' && url[pos+1] != 0 && url[pos+1] == '/' && url[pos+2] != 0 && url[pos+1] =='/')
		{
			/// 分析协议类型
			if(aranya_strnicmp(url, "http", pos) == 0)
			{
				this->scheme = SCHEME_HTTP;
				this->port = HTTP_PORT;
			}
			else if(aranya_strnicmp(url, "ftp", pos) == 0)
			{
				this->scheme = SCHEME_FTP;
				this->port = FTP_PORT;
			}
			else if(aranya_strnicmp(url, "file", pos) == 0)
			{
				this->scheme = SCHEME_FILE;
			}
			else
			{
				this->scheme = SCHEME_OTHER;
			}

			/// 参数url去除协议头
			if(cut)
			{
				_int_32 idx = 0;
				pos += 3;
				while(url[pos] != 0)	/// 把"://"后面的所有字符移到第0索引开始处
				{
					url[idx++] = url[pos++];
				}
				url[idx] = 0;
			}
		}
		else	/// 本身不带有协议头
		{
			this->scheme = SCHEME_HTTP;
		}
	}
	else
	{
		return false;
	}
	return true;
}
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/// 整理文件路径
/// @param file：文件字符串
/// @return 成功与否
bool url::arrange_file()
{
	if(this->file)
	{
		/////////////////////////
		/// cout << "arrangefile: " << this->file << endl;
		/////////////////////////
		_int_32 pos = 0;

		/// '#'是分片标识，不需要写到GET的file里，
		while(this->file[pos] != 0 && this->file[pos] != '#')
		{
			if(this->file[pos] == '/')
			{
				/// 字符串中间出现"/./"合并成"/"(本目录)
				if(this->file[pos+1] != 0 && this->file[pos+1] == '.'
						&& this->file[pos+2] != 0 && this->file[pos+2] == '/')
				{
					_int_32 idx = pos + 3;
					while(this->file[idx] != 0)
					{
						this->file[idx-2] = this->file[idx];
						++idx;
					}
					this->file[idx] = 0;
					++pos;
				}
				/// 字符串中间出现"a/xxx/../"，合并成"a/"(上层目录)
				else if(this->file[pos+1] != 0 && this->file[pos+1] == '.'
							&& this->file[pos+2] != 0 && this->file[pos+2] == '.'
							&& this->file[pos+3] != 0 && this->file[pos+3] == '/')
				{
					_int_32 idx = pos + 4;	/// "/../"后面的第一个元素(须检查是否结束)
					--pos;
					/// 找到上一个'/'
					while(pos >= 0 && this->file[pos] != '/')
						--pos;
					/// 根目录还要向上一层，错误
					if(pos <= 0)
					{
						/////////////////////////
						cout << "[arrange_file_error]file: " << this->file << ":"  << endl
									<< "	this file is not valid" << endl << endl;
						/////////////////////////
						return false;
					}

					_int_32 dec = idx - pos - 1;	/// 相差值
					while(this->file[idx] != 0)
					{
						this->file[idx-dec] = this->file[idx];
						++idx;
					}
					this->file[idx-dec] = 0;
					/// 不进行++pos，使pos指向'/'，依此来判断后面的字符串
				}
				/// 字符中出现“//”，合并成'/'
				else if(this->file[pos+1] != 0 && this->file[pos+1] == '/')
				{
					_int_32 idx = pos +1;
					while(this->file[idx] != 0)
					{
						this->file[idx] = this->file[idx+1];
						++idx;
					}
					/// 不进行++pos，使pos指向'/'，来配合处理没有处理的字符串
				}
				/// 字符尾部出现"/."，去掉最后一个点
				else if(this->file[pos+1] == '.' && this->file[pos + 2] == 0)
				{
					this->file[pos+1] = 0;
					return true;
				}
				/// 字符尾部出现"a/xxx/.."，回到上层目录变为"a/"
				else if(this->file[pos+1] != 0 && this->file[pos+1] == '.'
							&& this->file[pos+2] != 0 && this->file[pos+2] == '.'
							&& this->file[pos+3] == 0)
				{
					--pos;
					/// 找到上一个'/'
					while(pos >= 0 && this->file[pos] != '/')
						--pos;
					/// 以"/.."开头，错误
					if(pos <= 0)
					{
						/////////////////////////
						cout << "[arrange_file_error]file: " << this->file << ":"  << endl
									<< "	this file is not valid" << endl << endl;
						/////////////////////////
						return false;
					}
					this->file[pos+1] = 0;
					return true;
				}
				else
				{
					++pos;
				}
			}
			/// 还原中文字符串
			else if(this->file[pos] == '%')
			{
				if(this->file[pos+1] != 0 && this->file[pos+2] != 0)
				{
					_int_32 pn_1 = hexc_to_i(this->file[pos+1]);
					_int_32 pn_2 = hexc_to_i(this->file[pos+2]);
					if(pn_1 < 0 || pn_2 < 0)
					{
						/////////////////////////
						cout << "[arrange_file_error]file: " << this->file << ":"  << endl
									<< "	this file is not valid because of bad char after '%' below 0" << endl << endl;
						/////////////////////////
						return false;
					}
					else
					{
						/// 全角字符(中文)
						char SBC = 16 * pn_1 + pn_2;
						/// 如果是可打印字符(除空格外)
						///if(isgraph(SBC))
						{
							_int_32 idx = pos + 3;
							this->file[pos] = SBC;
							/// 整理，移动'%'位置以后的字符
							while(this->file[idx] != 0)
							{
								this->file[idx - 2] = this->file[idx];
								++idx;
							}
							this->file[idx-2] = 0;
						}
/*
						else if(SBC == ' ' || SBC == '/')
						{
							/// 不处理，空格和'/'在文件名(或文件夹)以%XX表示
							pos += 3;
						}
						else
						{
							/////////////////////////
							cout << "file:" << file << ":"  << endl
										<< "	this file is not valid because of bad char after '%', the char is " << SBC << "(16*" << pn_1 << "+" << pn_2 << ")" << endl << endl;
							/////////////////////////
							return false;
						}
 */
					}
				}
				++pos;
			}
			else 
			{
				++pos;
			}

		}

		/// 一定以'#'结束的file
		if(this->file[pos] != '#' && pos + 1 < sizeof(this->file))
		{
			this->file[pos] = '#';
			this->file[pos+1] = 0;
		}
		/// 必须以'/'开头
		if(this->file[0] != '/')
		{
			_int_32 pos = strlen(this->file);
			while(pos >= 0)
			{
				this->file[pos+1] = this->file[pos];
				--pos;
			}
			this->file[0] = '/';
		}
	}
	return true;
}
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/// 解析url，分解成协议，用户名，密码，主机，端口，文件路径六个部分
/// @param url：待解析的url字符串
/// @return 是否成功
bool url::parse_url(char *url)
{
	/// url不空，不超过长度
	if(url && strlen(url) < MAX_URL_SIZE)
	{
		_int_32 sta = 0, end = 0;

		/// 得到并去掉协议头
		if(this->parse_scheme(url))
		{
			/// 找到用户名密码的区分点'@'或找到文件路径的区分点'/'
			while(url[end] != 0 && url[end] != '/' && url[end] != '@')
				++end;
			/// url包含用户名密码部分
			if(url[end] == '@')
			{
				_int_32 idx = 0;
				/// 寻找用户名和密码的分隔符':'，并检查用户名字段的长度是否合法
				while(idx < end && idx < MAX_USER_SIZE - 1 && url[idx] != 0 && url[idx] != ':')
					++idx;
				/// 找到":"，语法正确，复制用户名和密码
				if(url[idx] == ':')
				{
					/// 得到用户名，从头开始复制，个数为idx
					strncpy(this->user, &(url[sta]), idx);
					this->user[idx] = 0;
					/// 得到密码，从sta+idx+1(也就是':'的下一个元素)开始，个数为‘@’的索引值减去复制开始的索引值
					strncpy(this->passwd, &(url[sta+idx+1]), end - sta - idx - 1);
					this->passwd[end-sta-idx] = 0;

					/// 下一部分检索的开始地址
					sta = end + 1;
					/// 找到文件路径区分点‘/'
					while(url[end] != 0 && url[end] != '/')
						++end;
				}
				else
				{
					/////////////////////////
					cout << "[parse_error]url: " << url << ":"  << endl
								<< "	the user/passwd of url is not valid" << endl << endl;
					/////////////////////////
					return false;
				}
			}

			/// 可能找到了'/'，也可以到了字符串尾部，这一部分是主机部分(可能包含端口)或者整个url只有主机部分
			_int_32 idx = sta;
			/// 在主机部分中找端口区分点':'，找不到则全为主机部分
			while(idx < end && url[idx] != ':')
				++idx;
			/// 从字符串里取出主机部分(不管找到没有找到':'，主机都是从sta开始到idx)
			if(idx < MAX_HOST_SIZE)
			{
				strncpy(this->host, &(url[sta]), idx - sta);
				this->host[idx-sta+1] = 0;
			}
			else
			{
				/////////////////////////
				cout << "url:" << url << ":"  << endl
							<< "	the host part of url is too long" << endl << endl;
				/////////////////////////
				return false;
			}

			/// 找到了端口区分点':'，取出端口部分
			if(idx < end)
			{
				this->port = atoi(&(url[idx+1]));
				//////////////////////////////
				/// cout << "parse_url port_s: " << &(url[idx+1]) << " port: " << this->port << endl;
				//////////////////////////////
				if(this->port > MAX_PORT_VALUE || this->port <= 0)
				{
					/////////////////////////
					cout << "[parse_error]url: " << url << ":"  << endl
								<< "	the port part of url is not valid" << endl << endl;
					/////////////////////////
					return false;
				}
			}

			/// 如果找到了'/'，表示可能有文件路径部分
			if(url[end] == '/')
			{
				if(strlen(url) - end < MAX_FILE_DIR_SIZE - 1)
				{
					strncpy(this->file, &(url[end]), strlen(url) - end);
					this->file[strlen(url)-end] = 0;
					/////////////////////////
					/// cout << "parse_url: " << this->file  << endl;
					/////////////////////////
				}
				else
				{
					/////////////////////////
					cout << "[parse_error]url: " << url << ":"  << endl
								<< "	the file part of url is too big" << endl << endl;
					/////////////////////////
					return false;
				}
			}
		}
	}
	else return false;

	return true;
}
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/// 返回主机类型(ip或域名)
/// @return 主机类型
_uint_8 url::host_kind()
{
	if(this->host)
	{
		_int_32 pos = 0, point_num = 0;
		/// 循环判断
		while(this->host[pos] != 0)
		{
			/// 点分十进制
			if(point_num > 2)
				return HOST_ERROR;

			if((this->host[pos] >= '0' && this->host[pos] <= '9') || this->host[pos] == '.')
			{
				if(this->host[pos] == '.')
					++point_num;
			}
			else
			{
				return HOST_DOMAIN;
			}
		}
		return HOST_IP;
	}
	return HOST_ERROR;
}
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/// 组成一个完整的url
/// @param url：返回的url
/// @return 是否成功
char* url::normalize_url()
{
	/// url = new char[strlen(this->host) + strlen(this->file) + strlen(this->user) + strlen(this->passwd) + 20 + 1];
	static char url[MAX_URL_SIZE];
	bzero(url, sizeof(url));
	char tmp[11];
	bzero(tmp, sizeof(tmp));

	/// 协议部分
	if(this->scheme == SCHEME_HTTP)
	{
		bzero(tmp, sizeof(tmp));
		strncpy(tmp, "http://", 10);
		strncat(url, tmp, sizeof(tmp));
	}
	else if(this->scheme == SCHEME_FTP)
	{
		bzero(tmp, sizeof(tmp));
		strncpy(tmp, "ftp://", 10);
		strncat(url, tmp, sizeof(tmp));
	}
	else if(this->scheme == SCHEME_FILE)
	{
		bzero(tmp, sizeof(tmp));
		strncpy(tmp, "file://", 10);
		strncat(url, tmp, sizeof(tmp));
	}
	else
	{
		////////////////////////////////
		cout << "not a supposed scheme" << endl << endl;
		////////////////////////////////
		return NULL;
	}

	/// 用户名密码
	if(strlen(this->user) || strlen(this->passwd))
	{
		strncat(url, this->user, sizeof(this->user));

		bzero(tmp, sizeof(tmp));
		strncpy(tmp, ":", sizeof(":"));
		strncat(url, tmp, sizeof(tmp));

		strncat(url, this->passwd, sizeof(this->passwd));

		bzero(tmp, sizeof(tmp));
		strncpy(tmp, "@", sizeof("@"));
		strncat(url, tmp, sizeof(tmp));
	}

	/// 主机
	if(this->host)
	{
		strncat(url, this->host, sizeof(this->host));
	}
	else
	{
		//////////////////////////
		cout << "error host" << endl << endl;
		//////////////////////////
		return NULL;
	}

	/// 端口
	if((this->scheme != SCHEME_HTTP && this->scheme != SCHEME_FTP)
			|| this->scheme == SCHEME_HTTP && this->port != HTTP_PORT
			|| this->scheme == SCHEME_FTP && this->port != FTP_PORT)
	{
		///////////////////
		/// cout << "normalize_url port: " << this->port << endl;
		///////////////////
		bzero(tmp, sizeof(tmp));
		snprintf(tmp, sizeof(tmp), ":%u", this->port);
		strncat(url, tmp, sizeof(tmp));
	}

	/// 文件路径
	if(this->file)
	{
		strncat(url, this->file, sizeof(this->file));
	}
	else
	{
		bzero(tmp, sizeof(tmp));
		strncpy(tmp, "/", 1);
		strncat(url, tmp, sizeof(tmp));
	}
	return url;
}
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/// 清除url的各个字段
/// @return NULL
//////////////////////////////////////////////////////////////////////
void url::clear_url()
{
	this->scheme = SCHEME_HTTP;
	this->port = HTTP_PORT;
	bzero(this->host, sizeof(this->host));
	bzero(this->file, sizeof(this->file));
	bzero(this->user, sizeof(this->user));
	bzero(this->passwd, sizeof(this->passwd));

}
